# communication module
# TODO 还没有实现可用版本
# 需要连接池来发消息
import os
import json
import protocol
import socket, time
from global_val import GlobalVal
import threading
from socketserver import BaseRequestHandler
import covar

# from time import sleep
# from functools import partial

# serversocket和socket链接可用的端口池
# 防止连接不上
# server_port_pool = [p for p in range(11101,11110)]
TIMEOUT = 10
BUF_SIZE = 1024  # 缓冲区大小


class MoveHandler(BaseRequestHandler):
    def __init__(self, request, client_address, server):
        super().__init__(request, client_address, server)

    def handle(self):
        # base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        # print("movehandler!!!!!!!!!!")
        # print(base_dir)
        filepath = '/attack/Automated-Attack-Defence-Test.zip'
        if os.path.exists(filepath):
            print("yyyyyyyyyyyy")
        with open(filepath, "rb") as f:
            while True:
                strb = f.read(1024)
                if strb == b"":
                    break
                self.request.send(strb)
        #self.client_address.remove(self.client_address)
        # self.client_socket.remove(self.request)


# 接收全局变量修改请求
class Handler(BaseRequestHandler):
    # def __init__(self, request, client_address, server):
    #     super().__init__(request, client_address, server)
    #     self.gv = GlobalVal('./GlobalVal.pickle')

    def __init__(self, event, *args, **kwargs):
        self.event = event
        self.gv = GlobalVal('./gv_data/GlobalVal.pickle')
        # BaseHTTPRequestHandler calls do_GET **inside** __init__ !!!
        # So we have to call super().__init__ after setting attributes.
        super().__init__(*args, **kwargs)

    def handle(self):
        buffer = str()
        while True:
            try:
                buffer += self.request.recv(BUF_SIZE).strip().decode()  # 接收套接字数据
                if buffer[-11:] == "[Terminate]":
                    break
                buffer = buffer[:-11]
                print("{} 发送请求:".format(self.client_address[0]))
                dic = json.loads(buffer)
                # 调用action属性值所对应的函数
                action = dic["action"]
                if hasattr(self, action):  # 判断对象有action属性
                    func = getattr(self, action)  # 把action属性值赋给func
                    func(dic)  # 调用属性值对应的函数，以dic作为传入的参数。相当于根据action属性值嵌入对应函数
            except Exception as e:
                return

    def update_order_info(self, *args):
        dic = args[0]
        order_info = dic["order_info"]
        timestamp = dic["timestamp"]
        if timestamp != self.gv.get_val("timestamp"):
            self.gv.set_val("timestamp", timestamp)
            self.gv.set_val("order_info", order_info)
            self.event.set()

    def update_topo_info(self, *args):
        dic = args[0]
        topo_info = dic["topo_info"]
        timestamp = dic["timestamp"]
        if timestamp != self.gv.get_val("timestamp"):
            self.gv.set_val("timestamp", timestamp)
            self.gv.set_val("topo_info", topo_info)

    def transfer_useful_info(self, *args):
        dic = args[0]
        source_ip = dic["source_ip"]
        filename = dic["filename"]
        filesize = dic["filesize"]
        timestamp = dic["timestamp"]
        if timestamp != self.gv.get_val("timestamp"):
            self.gv.set_val("timestamp", timestamp)
            filepath = './useful_data/' + source_ip + '/' + filename
            with open(filepath, "wb") as f:
                recentreceived = 0
                while recentreceived < filesize:
                    data = self.request.recv(BUF_SIZE)
                    f.write(data)
                    recentreceived += len(data)

    def awake(self, *args):
        dic = args[0]
        timestamp = dic["timestamp"]
        if timestamp != self.gv.get_val("timestamp"):
            self.gv.set_val("timestamp", timestamp)
            self.event.set()


# 用于广播全局变量修改请求
# 注意每次调用时先通过connect建立连接，需要根据拓扑结构决定ip、port
class FTPClient(object):
    def __init__(self):
        self.client = socket.socket()

    # def connect(self, ip, port):
    def connect(self, ip):
        # 建立socket连接
        self.client.settimeout(TIMEOUT)
        self.client.connect((ip, 55555))

    def exit(self):
        print("已断开socket")
        exit()

    def _send(self, msg_dic):
        print("发送请求：" + str(msg_dic))
        self.client.send((json.dumps(msg_dic) + "[Terminate]").encode())

    # def update_ip(self, ip):
    #     msg_dic = protocol.Protocol.json_update_ip(ip=ip)
    #     self._send(msg_dic)

    def update_order_info(self, order_info_list, timestamp):
        order_info = str(order_info_list)
        msg_dic = protocol.Protocol.json_update_order_info(order_info=order_info, timestamp=timestamp)
        self._send(msg_dic)

    def update_topo_info(self, topo_info, timestamp):
        msg_dic = protocol.Protocol.json_update_topo_info(topo_info=topo_info, timestamp=timestamp)
        self._send(msg_dic)

    def transfer_useful_info(self, source_ip, filepath, timestamp):
        # 检查文件路径是否存在
        if not os.path.exists(filepath):
            print("该文件路径不存在！")
        else:
            filename = os.path.basename(filepath)
            filesize = os.path.getsize(filepath)
            msg_dic = protocol.Protocol.json_transfer_useful_info(source_ip=source_ip, filename=filename, filesize=filesize, timestamp=timestamp)
            with open(filepath, "rb") as f:
                while True:
                    strb = f.read()
                    if strb == b"":
                        break
                    self.client.send(strb)
            self._send(msg_dic)

    def wakeup(self, timestamp):
        msg_dic = protocol.Protocol.json_wakeup(timestamp=timestamp)
        self._send(msg_dic)


def broadcast_newtopo(newtopo, timestamp):
    gv = GlobalVal('./gv_data/GlobalVal.pickle')
    topo_dict = eval(gv.get_val("topo", {}))
    self_ip_list = covar.get_val("self_ip")
    for self_ip in self_ip_list:
        for ip in topo_dict[self_ip]:
            if ip != self_ip and ip in eval(gv.get_val('attacked_ips_list')):
                client = FTPClient()
                print("connecting ip:", ip, "to broadcast new topo from", self_ip)
                client.connect(ip)
                client.update_topo_info(newtopo, timestamp)


def broadcast_neworder(neworder, timestamp):
    gv = GlobalVal('./gv_data/GlobalVal.pickle')
    topo_dict = eval(gv.get_val("topo", {}))
    self_ip_list = covar.get_val("self_ip")
    for self_ip in self_ip_list:
        for ip in topo_dict[self_ip]:
            if ip != self_ip and ip in eval(gv.get_val('attacked_ips_list')):
                client = FTPClient()
                print("connecting ip:", ip, "to broadcast new order from", self_ip)
                client.connect(ip)
                client.update_order_info(neworder, timestamp)


def broadcast_useful_info(source_ip_list, timestamp):
    gv = GlobalVal('./gv_data/GlobalVal.pickle')
    for i in range(len(source_ip_list)):
        source_ip = source_ip_list[i]
        basepath = './useful_data/' + source_ip + '/'
        topo_dict = eval(gv.get_val("topo", {}))
        self_ip_list = covar.get_val("self_ip")
        for self_ip in self_ip_list:
            for ip in topo_dict[self_ip]:
                if ip != self_ip and ip in eval(gv.get_val('attacked_ips_list')):
                    client = FTPClient()
                    print("connecting ip:", ip, "to broadcast useful info from", self_ip)
                    client.connect(ip)
                    for root, dirs, files in os.walk(basepath):
                        for f in files:
                            client.transfer_useful_info(source_ip, os.path.join(root, f), timestamp)


def broadcast_wakeup(timestamp):
    gv = GlobalVal('./gv_data/GlobalVal.pickle')
    topo_dict = eval(gv.get_val("topo", {}))
    self_ip_list = covar.get_val("self_ip")
    for self_ip in self_ip_list:
        for ip in topo_dict[self_ip]:
            if ip != self_ip and ip in eval(gv.get_val('attacked_ips_list')):
                client = FTPClient()
                print("connecting ip:", ip, "to broadcast new order from", self_ip)
                client.connect(ip)
                client.wakeup(timestamp)


# if __name__ == "__main__":
#     HOST, PORT = "localhost", 53333
#     server = ThreadingTCPServer((HOST, PORT), MoveHandler)
#     server.serve_forever()



# 测试一下
# set_val("conn_pool", [])
# start_socket_server_old()
# time.sleep(3000)

# thread = MainThread()
# thread.start()
